#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>

int main(int argc, char* argv[])
{
    if(argc != 2) {
        printf("file number error\n");
        exit(1);
    }
    
    // 调用 stat 得到文件属性
    // 解析属性信息: st_mode、uid、gid、time
    struct stat sb;
    // stat(argv[1], &sb);    // 穿透
    lstat(argv[1], &sb);      // 不穿透

    // st_mode
    char stmode[11] = {0};
    memset(stmode, '-', sizeof(stmode)-1);

    // 处理文件类型
    if(S_ISREG(sb.st_mode))  stmode[0] = '-'; 
    if(S_ISDIR(sb.st_mode))  stmode[0] = 'd'; 
    if(S_ISCHR(sb.st_mode))  stmode[0] = 'c'; 
    if(S_ISBLK(sb.st_mode))  stmode[0] = 'b'; 
    if(S_ISFIFO(sb.st_mode)) stmode[0] = 'p'; 
    if(S_ISLNK(sb.st_mode))  stmode[0] = 'l'; 
    if(S_ISSOCK(sb.st_mode)) stmode[0] = 's'; 

    // 解析权限
    if(sb.st_mode & S_IRUSR) stmode[1] = 'r';  // 所属用户读权限
    if(sb.st_mode & S_IWUSR) stmode[2] = 'w';  // 所属用户写权限
    if(sb.st_mode & S_IXUSR) stmode[3] = 'x';  // 所属用户执行权限
    

    if(sb.st_mode & S_IRGRP) stmode[4] = 'r';  // 所属组读权限
    if(sb.st_mode & S_IWGRP) stmode[5] = 'w';  // 所属组写权限
    if(sb.st_mode & S_IXGRP) stmode[6] = 'x';  // 所属组执行权限


    if(sb.st_mode & S_IROTH) stmode[7] = 'r';  // 所属其他用户读权限
    if(sb.st_mode & S_IWOTH) stmode[8] = 'w';  // 所属其他用户写权限
    if(sb.st_mode & S_IXOTH) stmode[9] = 'x';  // 所属其他用户执行权限

    // 用户名和组名，可以通过 getpwuid 和 getgrgid 获得
    // 时间的分析: 
    struct tm* filetm = localtime(&sb.st_atim.tv_sec);  // #define st_atime st_atim.tv_sec
    char timebuf[20] = {0};
    sprintf(timebuf, "%2d月 %d %02d:%02d", filetm->tm_mon+1, filetm->tm_mday, filetm->tm_hour, filetm->tm_min);

    // -rw-r--r-- 1 xiaodi xiaodi   140 May 14 18:13 Makefile
    printf("%s %d %s %s %d %s %s\n", stmode, sb.st_nlink, getpwuid(sb.st_uid)->pw_name, 
                                     getgrgid(sb.st_gid)->gr_name, sb.st_size, timebuf, argv[1]);

    return 0;
}

